<template>
  <div class="container" ref="container">
    <canvas ref="canvas"></canvas>
    <div class="info" ref="info">
      <el-input type="number" v-model="nowLookModel.x"></el-input>
      <el-input type="number" v-model="nowLookModel.y"></el-input>
      <el-input type="number" v-model="nowLookModel.z"></el-input>
    </div>
  </div>
</template>

<script>
  import * as THREE from 'three'

  import {OrbitControls} from '@/modules/examples/jsm/controls/MyOrbitControls.js';
  import {TWEEN} from 'three/examples/jsm/libs/tween.module.min.js'
  import {GUI} from 'three/examples/jsm/libs/dat.gui.module.js';

  import Stats from 'three/examples/jsm/libs/stats.module.js';

  import {GLTFLoader} from "three/examples/jsm/loaders/GLTFLoader.js";
  import {DRACOLoader} from 'three/examples/jsm/loaders/DRACOLoader.js'
  import {Sky} from "three/examples/jsm/objects/Sky";
  import {EffectComposer} from "three/examples/jsm/postprocessing/EffectComposer";
  import {RenderPass} from "three/examples/jsm/postprocessing/RenderPass";
  import {OutlinePass} from "three/examples/jsm/postprocessing/OutlinePass";
  import {ShaderPass} from "three/examples/jsm/postprocessing/ShaderPass";
  import {FXAAShader} from "three/examples/jsm/shaders/FXAAShader";

  export default {
    name: "HelloThree",
    mixins: [],
    data() {
      return {
        headerHeight: 0,
        roamsMove: {up: false, down: false, left: false, right: false},
        // ======three.js--data========
        scene: null, // 场景
        camera: null,  // 相机
        renderer: null, // 加载器
        modelData: null,
        container: {}, // 容器w，h，aspect
        composer: null, outlinePass: null, effectFXAA: null, selectedObjects: null, // 后期处理
        light: null, cubeCamera: null, parameters: null, water: null, // 环境-天空盒-水
        nowLookModel: {x: 0, y: 0, z: 0}, // 控制当前你看到的东西
        orbitControls: null,// 控制器
        stats: new Stats(), // 性能检测
        raycaster: new THREE.Raycaster(), // 拾取
        mouse: new THREE.Vector2(), // 鼠标的位置
        mouseMap: {}, // 鼠标点击地图位置
        downTargetName: null, // onDown 选中的目标
        isMove: false, // 触发点击时是否移动了
        isZoom: false, // 判断是否在缩放，true：在放大缩小，false:什么都没做
        isRoams: true, // 是否自动漫游移动
        touchMoveTimeStamp: 0, // 最后一次触摸移动的时间
      }
    },
    mounted() {
      this.initBaseCSR(); // 初始化相机 初始化场景  初始化渲染器
      this.initControls(); // 初始化控制器
      // this.initPostprocessing(); // 后期处理
      this.initLight(); // 初始化灯光
      this.initHelper(); // 初始化辅助线
      this.initSkyBox(); // 初始化天空环境
      this.initModel(); // 初始化模型
      this.addEL(); // 添加监听器
      this.animate(); // 动画
    },
    methods: {
      initBaseCSR() {
        this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);

        this.scene = new THREE.Scene();

        this.renderer = new THREE.WebGLRenderer({canvas: this.$refs['canvas'], antialias: true, alpha: false});
        this.renderer.outputEncoding = THREE.sRGBEncoding
        this.renderer.setPixelRatio(window.devicePixelRatio);
        this.camera.updateProjectionMatrix();
        this.renderer.setSize(window.innerWidth, window.innerHeight, false);
      }, // 初始化相机 初始化场景  初始化渲染器
      _id(_id) {
        if (!_id) return this.renderer.domElement
        return document.querySelector(_id)
      }, // 使用this.renderer.domElement或者根据id获取对象
      initControls() {
        const orbitControls = new OrbitControls(this.camera, this.renderer.domElement);
        orbitControls.target.set(0, 0, 0.1)

        // orbitControls.maxDistance = 200
        // orbitControls.minDistance = 0.01

        orbitControls.rotateSpeed = 0.5

        orbitControls.enablePan = true // 启用平移
        orbitControls.enableZoom = true // 启用缩放
        orbitControls.enableDamping = true // 启用阻尼
        orbitControls.enableRotate = true // 启用旋转

        orbitControls.keyPanSpeed = 5000 // 移动速度

        orbitControls.enableKeys = true // 启用键盘

        orbitControls.keys = {
          LEFT: 65, //left arrow 37
          UP: 87, // up arrow 38
          RIGHT: 68, // right arrow 39
          BOTTOM: 83 // down arrow 40
        }

        this.orbitControls = orbitControls
      }, // 初始化控制器
      initSkyBox() {
        this.light = new THREE.DirectionalLight(0xFFFFFF, 0);
        this.scene.add(this.light);

        this.scene.add(this.light.target);

        let sky = new Sky();
        let uniforms = sky.material.uniforms;
        uniforms['turbidity'].value = 10;
        uniforms['rayleigh'].value = 2;
        uniforms['luminance'].value = 1;
        uniforms['mieCoefficient'].value = 0.005;
        uniforms['mieDirectionalG'].value = 0.8;
        this.parameters = {distance: 4, inclination: 0.25, azimuth: 0.25};

        this.cubeCamera = new THREE.CubeCamera(0.1, 1, 512);
        this.cubeCamera.renderTarget.texture.generateMipmaps = true;
        this.cubeCamera.renderTarget.texture.minFilter = THREE.LinearMipmapLinearFilter;
        this.scene.background = this.cubeCamera.renderTarget;

        let self = this

        function updateSun() {
          let theta = Math.PI * (self.parameters.inclination - 0.5);
          let phi = 2 * Math.PI * (self.parameters.azimuth - 0.5);
          self.light.position.x = self.parameters.distance * Math.cos(phi) * 200;
          self.light.position.y = self.parameters.distance * Math.sin(phi) * Math.sin(theta) * 200;
          self.light.position.z = self.parameters.distance * Math.sin(phi) * Math.cos(theta) * 200;
          sky.material.uniforms['sunPosition'].value = self.light.position.copy(self.light.position);
          self.cubeCamera.update(self.renderer, sky);
        } // 太阳
        updateSun();

        function initHelper(parameters) {
          let gui = new GUI();
          let folder = gui.addFolder('Sky');
          folder.add(parameters, 'inclination', 0, 2, 0.001).onChange(updateSun);
          folder.add(parameters, 'azimuth', 0, 2, 0.01).onChange(updateSun);
          folder.open();
        } // 辅助器
        // initHelper(this.parameters)
      }, // 初始化天空环境
      onWindowResize() {
        this.renderer.setPixelRatio(window.devicePixelRatio);
        this.camera.aspect = this._id().offsetWidth / this._id().offsetHeight
        this.camera.updateProjectionMatrix();
        this.renderer.setSize(window.innerWidth, window.innerHeight, false);
      }, // 监听容器大小
      setMouseXY(event) {
        event.preventDefault();
        if (event.targetTouches) {
          let left = Math.abs(this.mouse.x - (event.changedTouches[0].pageX / this.renderer.domElement.offsetWidth) * 2 + 1) / (event.timeStamp - this.touchMoveTimeStamp)
          let top = Math.abs(this.mouse.y + (event.changedTouches[0].pageY / this.renderer.domElement.offsetHeight) * 2 - 1) / (event.timeStamp - this.touchMoveTimeStamp)
          if (left > 0.0003 || top > 0.0003) {
            this.isMove = true;
            this._id().removeEventListener('touchmove', this.onMove)
          }

          this.mouse.x = (event.changedTouches[0].pageX / this._id().offsetWidth) * 2 - 1;
          this.mouse.y = -(event.changedTouches[0].pageY / this._id().offsetHeight) * 2 + 1;
          this.touchMoveTimeStamp = event.timeStamp
        } else { // 鼠标
          this.mouse.x = (event.offsetX / this._id().offsetWidth) * 2 - 1;
          this.mouse.y = -(event.offsetY / this._id().offsetHeight) * 2 + 1;
          if (Math.abs(event.movementX) > 1 || Math.abs(event.movementY) > 1) {
            this.isMove = true;
            this._id().removeEventListener('mousemove', this.onMove)
          }
        }
      }, // 设置mouse的坐标
      addEL() {
        window.addEventListener('resize', this.onWindowResize, false);

        // 监听触摸的事件
        this._id().addEventListener('touchstart', (event) => {
          this.onDown(event);
          this._id().addEventListener('touchmove', this.onMove, false); // 运行

          this._id().addEventListener('touchend', (event) => {
            this._id().removeEventListener('touchmove', this.onMove)
            this.onUp(event);
          }, {capture: false, once: true}); // 结束
        }, false); // 触摸开始

        // 监听鼠标的事件
        this._id().addEventListener('mousedown', (event) => {
          this.onDown(event)

          this._id().addEventListener('mousemove', this.onMove, false); // 运行

          this._id().addEventListener('mouseup', (event) => {
            this._id().removeEventListener('mousemove', this.onMove)
            this.onUp(event);
          }, {capture: false, once: true}); // 结束
        }, false); // 鼠标点击开始
      }, // 添加监听器
      initLight() {
        let ambientLight = new THREE.AmbientLight(0xffffff, 2);
        ambientLight.position.set(200, 200, 200);
        this.scene.add(ambientLight);

        let directionalLight = new THREE.DirectionalLight(0xffffff, 1);
        directionalLight.position.set(100, 200, 100);
        this.scene.add(directionalLight);
      }, // 初始化灯光
      initHelper() {
        // let grid = new THREE.GridHelper(2000, 20, 0x000000, 0x000000);
        // this.scene.add(grid);
        // let axesHelper = new THREE.AxesHelper(100);
        // this.scene.add(axesHelper)

        let gui = new GUI();

        console.log(this.scene)
        const folder = gui.addFolder('scene');

        const position = folder.addFolder('position')
        position.add(this.nowLookModel, 'x', -100, 100, 0.01)
        position.add(this.nowLookModel, 'y', -100, 100, 0.01)
        position.add(this.nowLookModel, 'z', -100, 100, 0.01)
        position.close()

        folder.close();

        this.$refs['container'].appendChild(this.stats.dom);
      }, // 初始化辅助线
      initPostprocessing() {
        this.composer = new EffectComposer(this.renderer);

        let renderPass = new RenderPass(this.scene, this.camera);
        this.composer.addPass(renderPass);

        this.outlinePass = new OutlinePass(new THREE.Vector2(window.innerWidth, window.innerHeight), this.scene, this.camera);
        this.composer.addPass(this.outlinePass);

        this.effectFXAA = new ShaderPass(FXAAShader);
        this.effectFXAA.uniforms['resolution'].value.set(1 / window.innerWidth, 1 / window.innerHeight);
        this.composer.addPass(this.effectFXAA);

        window.addEventListener('mousemove', onTouchMove);
        window.addEventListener('touchmove', onTouchMove);
        let self = this

        function onTouchMove(event) {
          let x, y;
          if (event.changedTouches) {
            x = event.changedTouches[0].pageX;
            y = event.changedTouches[0].pageY;
          } else {
            x = event.clientX;
            y = event.clientY;
          }
          self.mouse.x = (x / self._id().offsetWidth) * 2 - 1;
          self.mouse.y = -(y / self._id().offsetHeight) * 2 + 1;
          checkIntersection();
        }

        function addSelectedObject(object) {
          self.selectedObjects = [];
          self.selectedObjects.push(object);
        }

        function checkIntersection() {
          self.raycaster.setFromCamera(self.mouse, self.camera);
          let intersects = self.raycaster.intersectObjects([self.scene], true);
          if (intersects.length > 0) {
            let selectedObject = intersects[0].object;
            addSelectedObject(selectedObject);
            self.outlinePass.selectedObjects = self.selectedObjects;
          } else {
            self.outlinePass.selectedObjects = [];
          }
        }
      }, // postprocessing 初始化后期处理
      initModel() {
        const gltfLoader = new GLTFLoader();
        const imageLoader = new THREE.TextureLoader();
        const texture = imageLoader.load('/static/files/images/floor.jpeg')
        texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
        texture.repeat.set(1, 1);

        const setRoughness = (children) => {
          if (children.material) {
            if (children.material) {
              children.material.roughness = 1;
            }
          }
          if (children.children) {
            children.children.map((child) => {
              setRoughness(child)
            })
          }
        } // 设置粗糙度
        const setTexture = (children) => {
          if (children.type === 'Group') {
            children.children.map((child) => {
              setTexture(child)
            })
          } else if (children.type === "Mesh") {
            children.children.map((child) => {
              test(child)
            })
            if (children.material) {
              if (children.material.length > 1) {
                children.material.forEach((material, index) => {
                  children.material[index].map = texture
                  // children.material[index].wireframe = true
                })
              } else {
                // children.material.wireframe = true
                children.material.map = texture
              }
            }
          }
        } // 递归贴图

        const onLoad = (gltf) => {
          gltf = gltf.scene
          gltf.name = "modelScene"
          console.log('gltf: ', gltf)
          this.modelData = new THREE.Group()
          this.modelData.add(gltf)

          setRoughness(gltf)

          this.scene.add(this.modelData)
        }
        const onProgress = (progress) => console.log('progress.loaded: ', progress, progress.loaded)
        const onError = (error) => console.error(error)

        const dracoLoader = new DRACOLoader();
        dracoLoader.setDecoderPath('/static/js/libs/draco/');
        gltfLoader.setDRACOLoader(dracoLoader);
        const fileUrl = this.$store.state.fileUrl.fileUrl || localStorage.getItem('fileUrl')
        gltfLoader.load(fileUrl, onLoad, onProgress, onError)
      }, // 初始化模型
      animate() {
        requestAnimationFrame(this.animate);
        this.render()
      }, // 动画
      render() {
        TWEEN.update();
        if (this.stats) this.stats.update();
        this.scene.position.set(...(Object.values(this.nowLookModel)))
        if (this.orbitControls) this.orbitControls.update();
        this.renderer.render(this.scene, this.camera);
        // this.composer.render(); // 后期的
      }, // 渲染
      onDown(event) {
        console.log('onDown: ', 'onDown');
        this.setMouseXY(event)
        this.isMove = false;
        this.raycaster.setFromCamera(this.mouse, this.camera);
        const intersectObject = this.raycaster.intersectObject(this.scene, true);
        console.log(intersectObject[0].point)
        if (intersectObject[0] && intersectObject[0].object.name.indexOf('热点') > -1) {
          this.downTargetName = intersectObject[0].object.name
        }
      }, // 鼠标或者触摸按下
      onMove(event) {
        console.log('onMove: ', 'onMove');
        this.setMouseXY(event)
        this.mapDirection = this.orbitControls.getAzimuthalAngle()
        this.raycaster.setFromCamera(this.mouse, this.camera);
      }, // 鼠标或者触摸移动
      onUp(event) {
        console.log('onUp: ', 'onUp================');
        this.setMouseXY(event)

        this.raycaster.setFromCamera(this.mouse, this.camera);
        const mouse = this.raycaster.intersectObject(this.scene, true);
        console.log(mouse[0].point)
        console.log('mouse: ', mouse)

        if (!this.isMove && mouse[0] && mouse[0].object.name.indexOf('DB') === 0) {
          this.orbitControls.enableRotate = false;

          new TWEEN.Tween(this.nowLookModel).to({
            x: this.nowLookModel.x - mouse[0].point.x,
            y: this.nowLookModel.y - mouse[0].point.y - 2,
            z: this.nowLookModel.z - mouse[0].point.z,
          }, 500).start();

          this.orbitControls.enableRotate = true;
        } else if (!this.isMove && mouse[0] && mouse[0].object.name.indexOf('HOT') === 0) {
          this.dialogVisibleImage = true
        }
      }, // 鼠标或者触摸弹起
    }
  }
</script>

<style lang="scss" scoped>
  .container {
    width: 100%;
    height: 100%;

    .info {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      z-index: 20;
    }

    canvas {
      display: block;
      width: 100%;
      height: 100%;
    }
  }
</style>
